Ctrl-C 做什么

  1. 用户输入 Ctrl-C
  2. 驱动程序收到字符
  3. 匹配 VINTR 和 ISIG 的字符被开启
  4. 驱动程序调用信号系统
  5. 信号系统发送 SIGINT 到进程
  6. 进程收到 SIGINT
  7. 进程消亡

信号是什么

每个信号都有一个数字编码, 终端信号通常是编码2

  1. 用户 用户能够通过输入 Ctrl-C 等请求内核产生信号
  2. 内核 当程序执行出错时, 内核给进程发送一个信号
  3. 进程 一个进程可以通过心痛调用 kill 给另一个进程发送信号

进程该如何处理信号

  1. 接受默认处理 signal(SIGINT, SIG_DEL);
  2. 忽略信号 signal(SIGINT, SIG_IGN);
  3. 调用一个函数 signal(signum, functionname);
  signal
目标 简单的信号处理
头文件 #include <signal.h>
函数原型 result = signal(int signum, void (*action)(int));
参数 signum 信号 /action 处理函数
返回值 -1 遇到错误, prevaction 成功

action 可以是函数名或一下两个特殊值之一:

signal 返回前一个处理函数. 值是指向函数的指针.

例子

1. 捕捉信号

sigdemo1.c

#include <stdio.h>
#include <signal.h>

void f(int);

int main()
{
    int i;
    signal(SIGINT, f);
    for (i = 0; i < 5; i++){
        printf("hello\n");
        sleep(1);
    }
    return 0;
}

void f(int signum)
{
    printf("OUCH!\n");
}

主函数由两个部分组成, 调用 signal 后进入一个循环. sigdemo1.c 调 用 signal 来设置 SIGINT 的处理函数f. 如果进程收到 SIGINT 信号, 内核会调用函数 f 来处理这个信号. 程序跳转到那个函数, 执行它的 代码, 然后返回跳转前的位置, 就像子过程调用一样.

2. 忽略信号

sigdemo2.c

#include <stdio.h>
#include <signal.h>

int main()
{
    signal(SIGINT, SIG_IGN);
    printf("you can't stop me!\n");
    while (1)
    {
        sleep(1);
        printf("haha\n");
    }
}
忽略了终端信号, 可以随意的按 Ctrl-C 而不会对进程产生影响.

为处理信号做准备: paly_again4.c

#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>

#define ASK "Do you want another transaction"
#define TRIES 3
#define SLEEPTIME 2
#define BEEP putchar('\a')

int get_response(char *, int);
void tty_mode(int);
void set_crmode();
void set_cr_noecho_mode();
void set_nodelay_mode();
char get_ok_char();
void ctrl_c_handler(int);

int main()
{
    int response;
    tty_mode(0);        /* save tty mode */
    set_cr_noecho_mode();  /* set -icanon, -echo */
    set_nodelay_mode();
    signal(SIGINT, ctrl_c_handler);
    signal(SIGQUIT, SIG_IGN);
    response = get_response(ASK, TRIES);
    tty_mode(1);        /* restore tty mode */
    return response;
}

int get_response(char *question, int maxtries)
{
    int input;
    printf("%s (y/n)?", question);
    while(1){
        sleep(SLEEPTIME);
        input = tolower(get_ok_char());
        if (input == 'y')
            return 0;

        if (input == 'n')
            return 1;

        if (maxtries-- == 0)
            return 2;
        BEEP;
    }
}

char get_ok_char()
{
    int c;
    while ((c = getchar()) != EOF && strchr("yYnN", c) == NULL)
        ;
    return c;
}

void set_cr_noecho_mode()
{
    struct termios ttystate;
    tcgetattr(0, &ttystate);
    ttystate.c_lflag     &= ~ICANON;  /* no buffering */
    ttystate.c_lflag      &= ~ECHO;    /* no echo either */
    ttystate.c_cc[VMIN]  = 1;   /* get 1 char at a time */
    tcsetattr(0, TCSANOW, &ttystate);
}

void set_nodelay_mode()
{
    int termflags;
    termflags = fcntl(0, F_GETFL);
    termflags |= O_NDELAY;
    fcntl(0, F_SETFL, termflags);
}

void tty_mode(int how)
{
    static struct termios original_mode;
    static int original_flags;
    if (how == 0){
        tcgetattr(0, &original_mode);
        original_flags = fcntl(0, F_GETFL);
    }
    else{
        tcsetattr(0, TCSANOW, &original_mode);
        fcntl(0, F_SETFL, original_flags);
    }
}

void ctrl_c_handler(int signum)
{
    tty_mode(1);
    exit(1);
}